#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QTimerEvent>
#include <QtGlobal>

#include "UIResponder_p.h"
#include "UIResponder.h"
#include "UIWindow.h"
#include "UIDevice.h"
#include "UITouch.h"

#define kTouchSwipeSpeed            (0.5)           /* Swipe Speed */
#define kTouchSwipeHoriz            (200)           /* Swipe Horiz Area */
#define kTouchSwipeVert             (46)            /* Swipe Vert Area */
#define kTouchHeldTime              (3000)          /* Held Time */
#define kTouchHeldArea              (20)            /* Held Area */
#define kTouchDoubleTapArea         (70)            /* Double Tap Area */
#define kTouchSingleTapArea         (25)            /* Single Tap Area */

#define kTouchEventTimeout          ((kUITouchMultiTapTime * 2) + 5)
#define kFlickEventTimeout          (20)
#define kFlickManualTimeout         (100)
#define kFlickTouchTreshold         (20)

#if UIKIT_SIMULATOR
    #define kFlickSpeedAttenuator       (4)
#else
    #define kFlickSpeedAttenuator       (2)
#endif

static QPointF flickDeaccelerate (const QPointF& speed) {
    qreal max = 64.0f;
    qreal a = 1.0f;

    qreal x = qBound(-max, speed.x(), max);
    qreal y = qBound(-max, speed.y(), max);
    qreal z = 0.0f;

    x = (x == z) ? x : (x > z) ? qMax(z, x - a) : qMin(z, x + a);
    y = (y == z) ? y : (y > z) ? qMax(z, y - a) : qMin(z, y + a);

    return(QPointF(x, y));
}

UIResponderPrivate::UIResponderPrivate (UIResponder *responder,
                                        UIWindow *window, 
                                        QGraphicsItem *view) 
    : QObject(), m_touchEvent(window, view)
{
    m_scrollEnabled = false;
    m_handleTouches = true;

    m_flickState = UIFlickStateSteady;
    m_flickTimestamp = QTime::currentTime();

    m_responder = responder;
}

UIResponderPrivate::~UIResponderPrivate() {
}

void UIResponderPrivate::touchesBegan (QGraphicsSceneMouseEvent *event) {
    if (m_touchTimer.isActive())
        m_touchTimer.stop();

    if (m_scrollEnabled)
        flickBegan(event);

    if (m_handleTouches) {
        m_touchEvent.start(event->scenePos());
        m_touchTimer.start(kTouchEventTimeout, this);
    }
}

void UIResponderPrivate::touchesMoved (QGraphicsSceneMouseEvent *event) {
    if (m_touchTimer.isActive())
        m_touchTimer.stop();

    if (!m_scrollEnabled || !flickMoved(event)) {
        if (m_handleTouches) {
            m_touchEvent.move(event->scenePos());
            m_touchTimer.start(kTouchEventTimeout, this);
        }
    }
}

void UIResponderPrivate::touchesEnded (QGraphicsSceneMouseEvent *event) {
    if (m_touchTimer.isActive())
        m_touchTimer.stop();

    if (!m_scrollEnabled || !flickEnded(event)) {
        if (m_handleTouches) {
            m_touchEvent.stop();
            m_touchTimer.start(kTouchEventTimeout, this);
        }
    }
}

bool UIResponderPrivate::flickBegan (QGraphicsSceneMouseEvent *event) {
    switch (m_flickState) {
        case UIFlickStateSteady:
            m_flickState = UIFlickStatePressed;
            m_flickTouchPos = event->scenePos();
            return(true);
        case UIFlickStateAutoScroll:
            m_flickState = UIFlickStateStop;
            m_flickSpeed = QPointF(0.0f, 0.0f);
            m_flickTouchPos = event->scenePos();
            m_flickOffset = m_responder->scrollOffset();
            m_flickTimer.stop();
            return(true);
        default:
            break;
    }

    return(false);
}

bool UIResponderPrivate::flickMoved (QGraphicsSceneMouseEvent *event) {
    QPointF delta;

    switch (m_flickState) {
        case UIFlickStatePressed:
        case UIFlickStateStop:
            delta = event->scenePos() - m_flickTouchPos;
            if (delta.x() > kFlickTouchTreshold || 
                delta.x() < -kFlickTouchTreshold ||
                delta.y() > kFlickTouchTreshold || 
                delta.y() < -kFlickTouchTreshold)
            {
                m_flickTimestamp = QTime::currentTime();
                m_flickState = UIFlickStateManualScroll;
                m_flickDelta = QPointF(0.0f, 0.0f);
                m_flickTouchPos = event->scenePos();
                return(true);
            }
            break;
        case UIFlickStateManualScroll:
            delta = event->scenePos() - m_flickTouchPos;
            m_responder->setScrollOffset(m_flickOffset - delta);
            if (m_flickTimestamp.elapsed() > kFlickManualTimeout) {
                m_flickTimestamp = QTime::currentTime();
                m_flickSpeed = delta - m_flickDelta;
                m_flickDelta = delta;
            }
            return(true);
        default:
            break;
    }

    return(false);
}

bool UIResponderPrivate::flickEnded (QGraphicsSceneMouseEvent *event) {
    QPointF delta;

    switch (m_flickState) {
        case UIFlickStatePressed:
            m_flickState = UIFlickStateSteady;
            return(true);
        case UIFlickStateManualScroll:
            delta = event->scenePos() - m_flickTouchPos;
            if (m_flickTimestamp.elapsed() > kFlickManualTimeout) {
                m_flickTimestamp = QTime::currentTime();
                m_flickSpeed = delta - m_flickDelta;
                m_flickDelta = delta;
            }
            m_flickOffset = m_responder->scrollOffset();
            m_flickTouchPos = event->scenePos();
            if (m_flickSpeed == QPointF(0.0f, 0.0f)) {
                m_flickState = UIFlickStateSteady;
            } else {
                m_flickSpeed /= kFlickSpeedAttenuator;
                m_flickState = UIFlickStateAutoScroll;

                if (!m_flickTimer.isActive())
                    m_flickTimer.start(kFlickEventTimeout, this);
            }
            return(true);
        case UIFlickStateStop:
            m_flickState = UIFlickStateSteady;
            m_flickOffset = m_responder->scrollOffset();
            return(true);
        default:
            break;
    }

    return(false);
}

void UIResponderPrivate::showTapArea (UITouch *touch) {
    if (touch->window() == NULL)
        return;

    m_tapItem.setRect(0.0f, 0.0f, kTouchSingleTapArea * 2, kTouchSingleTapArea * 2);
    m_tapItem.setZValue(2.0f);
    m_tapItem.setBrush(QColor(0xFF, 0xFF, 0xFF, 98));
    m_tapItem.setPen(Qt::NoPen);

    if (m_tapItemTimer.isActive()) {
        m_tapItemTimer.stop();
    } else {
        m_tapItem.setPos(touch->currentPos() - QPointF(kTouchSingleTapArea, kTouchSingleTapArea));
        touch->window()->addItem(&m_tapItem);
    }

    m_tapItemTimer.start(350, this);
}

void UIResponderPrivate::timerEvent (QTimerEvent *event) {
    if (event->timerId() == m_touchTimer.timerId()) {
        touchEvent();
        m_touchTimer.stop();
    } else if (event->timerId() == m_flickTimer.timerId()) {
        flickEvent();
    } else if (event->timerId() == m_tapItemTimer.timerId()) {
        m_touchEvent.window()->removeItem(&m_tapItem);
        m_tapItemTimer.stop();
    }

    QObject::timerEvent(event);
}

void UIResponderPrivate::touchEvent (void) {
    switch (m_touchEvent.phase()) {
        case UITouchPhaseBegan:
            m_responder->touchesBegan(&m_touchEvent);
            break;
        case UITouchPhaseMoved:
            m_responder->touchesMoved(&m_touchEvent);
            break;
        case UITouchPhaseEnded:
            m_responder->touchesEnded(&m_touchEvent);
            break;
        case UITouchPhaseCancelled:
            m_responder->touchesCancelled(&m_touchEvent);
            break;
    }
}

void UIResponderPrivate::flickEvent (void) {
    if (m_flickState == UIFlickStateAutoScroll) {
        m_flickSpeed = flickDeaccelerate(m_flickSpeed);
        m_responder->setScrollOffset(m_flickOffset - m_flickSpeed);
        m_flickOffset = m_responder->scrollOffset();

        if (m_flickSpeed == QPointF(0.0f, 0.0f)) {
            m_flickState = UIFlickStateSteady;
            m_flickTimer.stop();
        }
    } else {
        m_flickTimer.stop();
    }
}

UIResponder::UIResponder (UIWindow *window, QGraphicsItem *view) 
    : d(new UIResponderPrivate(this, window, view))
{
}

UIResponder::~UIResponder() {
    delete d;
}

bool UIResponder::scrollEnabled (void) const {
    return(d->scrollEnabled());
}

void UIResponder::setScrollEnabled (bool enabled) {
    d->setScrollEnabled(enabled);
}

bool UIResponder::handleTouches (void) const {
    return(d->handleTouches());
}

void UIResponder::setTouchesEnabled (bool enabled) {
    d->setTouchesEnabled(enabled);
}

void UIResponder::touchesBegan (UITouch *touch) {
    Q_UNUSED(touch)

    d->showTapArea(touch);
}

void UIResponder::touchesMoved (UITouch *touch) {
    Q_UNUSED(touch)
}

#include <QDebug>
void UIResponder::touchesEnded (UITouch *touch) {
	qreal dxAbs = touch->absDeltaX();
	qreal dyAbs = touch->absDeltaY();

    qDebug() << "TOUCH End" << touch->timeElapsed() << touch->tapCount() << dxAbs << dyAbs;
	if (dxAbs >= kTouchSwipeHoriz && dyAbs <= kTouchSwipeVert) {
		// It appears to be Swipe Event
		if ((dxAbs / touch->timeElapsed()) >= kTouchSwipeSpeed)
			swipeEvent(touch);
    } else if (touch->timeElapsed() > kTouchHeldTime && 
               dxAbs <= kTouchHeldArea && dyAbs <= kTouchHeldArea)
	{
		// It appears to be an Hold Event
		touchHeldEvent(touch);
	} else if (	touch->tapCount() > 1 &&
				dxAbs < kTouchDoubleTapArea && dyAbs < kTouchDoubleTapArea)
	{
		// It appears to be a Multi-Tap Event
		if (touch->tapCount() == 2)
			doubleTapEvent(touch);
		else
			multiTapEvent(touch);
	} else if (	touch->tapCount() == 1 &&
				dxAbs < kTouchSingleTapArea && dyAbs < kTouchSingleTapArea)
	{
		// It appears to be a Single Tap Event
		singleTapEvent(touch);
	}
}

void UIResponder::touchesCancelled (UITouch *touch) {
    Q_UNUSED(touch)
}


void UIResponder::swipeEvent (UITouch *touch) {
    Q_UNUSED(touch)
    qDebug(" - TOUCH Gestures: Swipe Event");
}

void UIResponder::multiTapEvent (UITouch *touch) {
    Q_UNUSED(touch)
    qDebug(" - TOUCH Gestures: Multi Tap Event");
}

void UIResponder::doubleTapEvent (UITouch *touch) {
    Q_UNUSED(touch)
    qDebug(" - TOUCH Gestures: Double Tap Event");
}

void UIResponder::singleTapEvent (UITouch *touch) {
    Q_UNUSED(touch)
    qDebug(" - TOUCH Gestures: Single Tap Event");
}

void UIResponder::touchHeldEvent (UITouch *touch) {
    Q_UNUSED(touch)
    qDebug(" - TOUCH Gestures: Touch Held Event");
}

QPointF UIResponder::scrollOffset (void) const {
    return(QPointF());
}
void UIResponder::setScrollOffset (const QPointF& pos) {
    Q_UNUSED(pos);
}

void UIResponder::touchReleaseEvent (QGraphicsSceneMouseEvent *event) {
    d->touchesEnded(event);

    // Propagate Release Event to the UIResponder Items
    if (QGraphicsScene *scene = dynamic_cast<QGraphicsScene *>(this)) {    
        foreach (QGraphicsItem *item, scene->items(event->scenePos())) {
            if (UIResponder *responder = dynamic_cast<UIResponder *>(item))
                responder->touchReleaseEvent(event);
        }
    }
}

void UIResponder::touchPressEvent (QGraphicsSceneMouseEvent *event) {
    d->touchesBegan(event);

    // Propagate Press Event to the UIResponder Items
    if (QGraphicsScene *scene = dynamic_cast<QGraphicsScene *>(this)) {
        foreach (QGraphicsItem *item, scene->items(event->scenePos())) {
            if (UIResponder *responder = dynamic_cast<UIResponder *>(item))
                responder->touchPressEvent(event);            
        }
    }
}

void UIResponder::touchMoveEvent (QGraphicsSceneMouseEvent *event) {
    d->touchesMoved(event);

    // Propagate Move Event to the UIResponder Items
    if (QGraphicsScene *scene = dynamic_cast<QGraphicsScene *>(this)) {
        foreach (QGraphicsItem *item, scene->items(event->scenePos())) {
            if (UIResponder *responder = dynamic_cast<UIResponder *>(item))
                responder->touchMoveEvent(event);
        }
    }
}

